/* thread_incr_psem.c

   Use a POSIX unnamed semaphore to synchronize access by two threads to
   a global variable.

   See also thread_incr.c and thread_incr_mutex.c.
*/
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static int glob = 0;
static sem_t sem;

static void errExit(char *msg) {
  perror(msg);
  exit(EXIT_FAILURE);
}

static void errExitEN(int errn, char *msg) {
  fprintf(stderr, "%s: %s\n", msg, strerror(errn));
  exit(EXIT_FAILURE);
}

/* Loop 'arg' times incrementing 'glob' */
static void *threadFunc(void *arg) {
  int loops = *((int *)arg);
  int loc, j;

  for (j = 0; j < loops; j++) {
    if (sem_wait(&sem) == -1) {
      errExit("sem_wait");
    }

    loc = glob;
    loc++;
    glob = loc;

    if (sem_post(&sem) == -1) {
      errExit("sem_post");
    }
  }

  return NULL;
}

int main(int argc, char *argv[]) {
  pthread_t t1, t2;
  int loops, s;

  loops = (argc > 1) ? atoi(argv[1]) : 10000000;

  /* Initialize a semaphore with the value 1 */
  if (sem_init(&sem, 0, 1) == -1) {
    errExit("sem_init");
  }

  /* Create two threads that increment 'glob' */
  s = pthread_create(&t1, NULL, threadFunc, &loops);
  if (s != 0) {
    errExitEN(s, "pthread_create");
  }
  s = pthread_create(&t2, NULL, threadFunc, &loops);
  if (s != 0) {
    errExitEN(s, "pthread_create");
  }

  /* Wait for threads to terminate */
  s = pthread_join(t1, NULL);
  if (s != 0) {
    errExitEN(s, "pthread_join");
  }
  s = pthread_join(t2, NULL);
  if (s != 0) {
    errExitEN(s, "pthread_join");
  }

  printf("glob = %d\n", glob);
  exit(EXIT_SUCCESS);
}